package org.rency.crawler.fetch.service.parser.impl;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashSet;
import javax.annotation.Resource;
import org.rency.crawler.common.exception.CrawlerException;
import org.rency.crawler.domain.http.FetchHttpRequest;
import org.rency.crawler.domain.http.FetchHttpResponse;
import org.rency.crawler.domain.queue.FetchQueue;
import org.rency.crawler.domain.vo.DocDomain;
import org.rency.crawler.fetch.helper.BuilderHelper;
import org.rency.crawler.fetch.helper.FetchCarrier;
import org.rency.crawler.fetch.repository.MetaResourceRepository;
import org.rency.crawler.fetch.service.parser.ParserService;
import org.rency.crawler.fetch.service.url.URLService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * ${todo}
 *
 * @author: Created by user_rcy@163.com on 2017/1/9.
 * @version: V1.0.0
 * @since JDK 1.8
 */
public abstract class AbstractParserService<Document> implements ParserService {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    protected MetaResourceRepository                      metaResourceRepository;

    @Resource
    protected URLService                                  urlService;

    @Override
    public void parser(FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection, HashSet<DocDomain> docQueueCollection) {
        String parserName = this.getClass().getSimpleName();
        logger.info("解析器[{}]解析URL[{}]种子.",parserName,carrier.getHttpRequest().getUrl());
        long start = System.currentTimeMillis();
        FetchHttpRequest request = carrier.getHttpRequest();
        FetchHttpResponse response = carrier.getHttpResponse();
        try{
            // 1.HTTP响应对象
            Document document = this.getDocument(request, response);

            // 2.页面是否支持抓取
            if(!this.fetchSupport(document,carrier.getHttpResponse())){
                logger.warn("页面不支持抓取[{}].",request.getUrl());
                return;
            }

            // 3.创建快照
            if(metaResourceRepository.generateSnapshot()){
                logger.trace("创建页面快照[{}],",request.getUrl());
                this.generateSnapshot(carrier,document);
            }

            // 4.提取URI
            this.parserHref(document,carrier,fetchQueueCollection);

            this.parserFrame(document,carrier,fetchQueueCollection);

            this.parserForm(document,carrier,fetchQueueCollection);

            if(metaResourceRepository.fetchImgSwitch()){
                this.parserImages(document,carrier,fetchQueueCollection);
            }

            if(metaResourceRepository.fetchStyleSwitch()){
                this.parserStyle(document,carrier,fetchQueueCollection);
            }

            if(metaResourceRepository.fetchScriptSwitch()){
                this.parserScript(document,carrier,fetchQueueCollection);
            }

            // 5.构建文档对象
            docQueueCollection.add(this.build(document, request, response, carrier));
        }catch(CrawlerException e){
            logger.error("解析器[{}]解析URL[{}]异常.",parserName,request.getUrl(),e);
        }catch(Exception e){
            logger.error("解析器[{}]解析URL[{}]未知异常.",parserName,request.getUrl(),e);
        }finally{
            logger.debug("解析器[{}]解析URL完成,共[{}],耗时[{}]ms",parserName,carrier.getFetchQueueCollection().size(),(System.currentTimeMillis() - start));
        }
    }

    /**
     * 解析HTTP响应
     * </br>Created on 2016年6月23日
     * @param request
     * @param response
     * @return HTTP响应文档对象
     */
    protected abstract Document getDocument(FetchHttpRequest request, FetchHttpResponse response) throws IOException;

    /**
     * 判断页面是否支持抓取
     * </br>Created on 2016年6月23日
     * @param document
     * @param response
     * @return
     */
    protected abstract boolean fetchSupport(Document document,FetchHttpResponse response);

    /**
     * 创建快照
     * </br>Created on 2016年6月23日
     * @param carrier
     * @param document
     */
    protected void generateSnapshot(FetchCarrier carrier,Document document){
        //TODO
    }

    /**
     * 提取Href超链接
     * </br>Created on 2016年6月23日
     * @param document
     * @param carrier
     * @param fetchQueueCollection
     * @return
     * @throws CrawlerException
     */
    protected abstract void parserHref(Document document, FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection) throws CrawlerException;

    /**
     * 提取IFrame浮动窗
     * </br>Created on 2016年6月27日
     * @param document
     * @param carrier
     * @param fetchQueueCollection
     * @return
     * @throws CrawlerException
     */
    protected abstract void parserFrame(Document document, FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection) throws CrawlerException;

    /**
     * 提取Form表单
     * </br>Created on 2016年6月23日
     * @param document
     * @param carrier
     * @param fetchQueueCollection
     * @return
     * @throws CrawlerException
     */
    protected abstract void parserForm(Document document,FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection) throws CrawlerException;

    /**
     * 提取Style样式
     * </br>Created on 2016年6月23日
     * @param document
     * @param carrier
     * @param fetchQueueCollection
     * @return
     * @throws CrawlerException
     */
    protected abstract void parserStyle(Document document,FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection) throws CrawlerException;

    /**
     * 提取Image
     * </br>Created on 2016年6月23日
     * @param document
     * @param carrier
     * @param fetchQueueCollection
     * @return
     * @throws CrawlerException
     */
    protected abstract void parserImages(Document document,FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection) throws CrawlerException;

    /**
     * 提取Script脚本
     * </br>Created on 2016年6月23日
     * @param document
     * @param carrier
     * @param fetchQueueCollection
     * @return
     * @throws CrawlerException
     */
    protected abstract void parserScript(Document document,FetchCarrier carrier, HashSet<FetchQueue> fetchQueueCollection) throws CrawlerException;

    private DocDomain build(Document document, FetchHttpRequest request, FetchHttpResponse response, FetchCarrier carrier){
        String fetchNo = carrier.getRequest().getFetchNo();
        String fetchUri = request.getUrl();
        String title = this.getPageTitle(document,response);
        String description = this.getPageDescription(document,response);
        String keywords = this.getPageKeywords(document,response);
        Charset charset = response.getCharset();
        byte[] html = this.getPageHtml(document,response);
        byte[] content = this.getPageText(document,response);
        return BuilderHelper.build(fetchNo, fetchUri, title, description, keywords, charset, html, content);
    }

    /**
     * 获取页面标题
     * @param document
     * @param response
     * @return
     */
    protected abstract String getPageTitle(Document document,FetchHttpResponse response);

    /**
     * 获取页面描述
     * @param document
     * @param response
     * @return
     */
    protected abstract String getPageDescription(Document document,FetchHttpResponse response);

    /**
     * 获取页面关键字
     * @param document
     * @param response
     * @return
     */
    protected abstract String getPageKeywords(Document document,FetchHttpResponse response);

    /**
     * 获取页面内容
     * @param document
     * @param response
     * @return
     */
    protected abstract byte[] getPageHtml(Document document,FetchHttpResponse response);

    /**
     * 获取页面文本内容
     * @param document
     * @param response
     * @return
     */
    protected abstract byte[] getPageText(Document document,FetchHttpResponse response);

    /**
     * 是否只抓取同一网站
     * @return
     */
    protected boolean fetchSameSite(){
        return metaResourceRepository.fetchSameSite();
    }
}
